/*
 * CloudBank Mobile Knowledge Sharing for Language Learners
 * Supported by the JISC Rapid Innovation Programme #jiscri
 *
 * Copyright (C) 2010 University of Brighton
 *
 * Licensed under the GNU General Public License v3 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.gnu.org/licenses/gpl.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package uk.ac.brighton.itrg.cloudbank;

import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.Attributes;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import android.database.AbstractCursor;

public class ArrayCursor extends AbstractCursor
{
	private static final String TAG = ArrayCursor.class.getSimpleName();

	String[] mCols;
	Object[][] mData;
	
	public ArrayCursor(String[] cols, Object[][] data)
	{
		super();

		// ----------------------------------------------------------				
		// what happens in super():
		// ----------------------------------------------------------				
		// mPos = -1;
		// mRowIdColumnIndex = -1;	// set this to support updates!!!
		// mCurrentRowID = null;
		// mUpdatedRows = new HashMap<Long, Map<String, Object>>();
		// ----------------------------------------------------------				

		Logger.log(TAG);
		mCols = cols;
		mData = data;	
	}

	public ArrayCursor(ArrayCursor c)
	{
		super();

		Logger.log(TAG);
		mCols = c.getColumnNames();
		mData = c.getData();	
	}

	public ArrayCursor(String[] cols, String xml) 
	{
		super();

		Logger.log(TAG);
		Logger.log("xml", xml);

		mCols = cols;
		mData = parse(xml);	
	}

	public String[] getColumnNames()
	{
		return mCols;
	}

	protected Object[][] getData()	
	{
		return mData;
	}
	
	public int getCount()
	{
		if(mData == null) return 0;
		return mData.length;
	}
	
	public long getLong(int column) 
	{
		long l = -1;
		if(mData == null) return l;
		
		String s = mData[mPos][column].toString();
		try
		{
			l = Long.parseLong(s);
		}
		catch(Exception e)
		{
			Logger.log("column", column);
			Logger.log("row", mPos);
			Logger.log("value", s);
			Logger.log(e);
		}

		return l;
	}

	public int getInt(int column)
	{
		int i = -1;
		if(mData == null) return i;
		
		String s = mData[mPos][column].toString();
		try
		{
			i = Integer.parseInt(s);
		}
		catch(Exception e)
		{
			Logger.log("column", column);
			Logger.log("row", mPos);
			Logger.log("value", s);
			Logger.log(e);
		}

		return i;
	}

	public String getString(int column)
	{
		if(mData == null) return null;
		return (mData[mPos][column]).toString();
	}
	
	
	public boolean isNull(int column)
	{
		if(mData == null) return true;
		return (null == mData[mPos][column]);
	}

	
// not supported --------------------------------------------------------------------
	public double getDouble(int column)
	{
		throw new UnsupportedOperationException("getDouble is not supported");
	}
	public float getFloat(int column)
	{
		throw new UnsupportedOperationException("getFloat is not supported");
	}
	public short getShort(int column)
	{
		throw new UnsupportedOperationException("getShort is not supported");
	}
// ----------------------------------------------------------------------------------
	
	
	
	private Object[][] parse(String xml) 
	{
		final int NIL = -2;
		final int ROW = -1;
		
		final ArrayList rows = new ArrayList();


        try
        {
		
	        SAXParserFactory spf = SAXParserFactory.newInstance();
	
	        SAXParser sp = spf.newSAXParser();
	        XMLReader xr = sp.getXMLReader();
	        xr.setContentHandler(new DefaultHandler() 
	        {
	        	int col = NIL;
	            Object[] row = null;
	            
	        	
	            public void startDocument() throws SAXException 
	            {
	                super.startDocument();
	            }
	
	            public void startElement(String uri, String localName, String name, Attributes atts) throws SAXException 
	            {
	                super.startElement(uri, localName, name, atts);
	                
	                if (localName.compareTo("error_response") == 0) 
	                {
	                	DB.setError(DB.ERR_SERVER);
	                } 
	                else if (localName.compareTo("row") == 0) 
	                {
	                	col = ROW;
	                    row = new Object[mCols.length];
	                    for(int i=0; i < mCols.length; i++)
	                    	row[i] = "";
	                } 
	                else if(col >= ROW)
	                {
	                    for(int i=0; i < mCols.length; i++)
	                    {
	                    	if (localName.compareTo(mCols[i]) == 0)
	                    	{
	                    		col = i;
	                    		break;
	                    	}
	                    }
	                }
	            }
	
	            public void endElement(String uri, String localName, String name) throws SAXException 
	            {
	                super.endElement(uri, localName, name);
	                
	                if (localName.compareTo("row") == 0)
	                {
	                    col = NIL;
	                    rows.add(row);
	                    row = null;
	                } 
	                else if(col >= ROW)
	                {
	                    for(int i=0; i < mCols.length; i++)
	                    {
	                    	if (localName.compareTo(mCols[i]) == 0)
	                    	{
	                    		col = ROW;
	                    		break;
	                    	}
	                    }
	                }
	            }
	
	            public void characters(char[] ch, int start, int length) throws SAXException
	            {
	                super.characters(ch, start, length);
	                
	                if (DB.getLastErrorCode() == DB.ERR_SERVER) 
	                {
	                	throw new SAXException(new String(ch, start, length));
	                }
	                else if (col > ROW) 
	                {
	                	String s = new String(ch, start, length);
	                	
	                	if(mCols[col].compareTo("_id") == 0)
	                	{
	                    	row[col] = new Long(s);
	                	}
	                	else
	                	{
	                		try
	                		{
	                			row[col] = URLDecoder.decode(s, CB.ENCODING);
	                		}
	                		catch(UnsupportedEncodingException e)
	                		{
	                			DB.setError(DB.ERR_ENCODING);
	                			throw new SAXException(e);
	                		}
	                	}
	                }
	            }
	
	        });
        
            xr.parse(new InputSource(new StringReader(xml)));

            // ------------------------------------------------
            // unfortunately Object data[][] = rows.toArray() 
            // does not compile and an explicit cast fails 
            // with a class cast exception (even if the list 
            // is typed to contain elements of type Object[])
            // 
            // so we have to do it by hand. but re performance, 
            // can this be done without allocating new memory ?
            // ------------------------------------------------
            Object data[][] = new Object[rows.size()][mCols.length]; 
            for(int i=0; i<rows.size(); i++)
            {
            	data[i] = (Object[])(rows.get(i));
            }
            return data;
        }
        catch(Exception e)
        {
        	DB.setError(DB.ERR_XMLPARSE);
        	Logger.log(e);
        }
        
        return null;
	}
	
}
